package com.sojson.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.apache.maven.model.Build;
import org.apache.maven.model.Model;
import org.apache.maven.model.io.xpp3.MavenXpp3Reader;

import com.sojson.constant.Constant;
import com.sojson.util.file.enums.EPackageType;
import com.sojson.util.properties.PropertiesUtil;

import lombok.extern.slf4j.Slf4j;

/**
 * Maven工具类
 * 
 * @author liu
 * @date 2021-04-28
 */
@Slf4j
public class MavenUtil {

    /** Model对象 */
    private static final Model MODEL;
    /**
     * 当前项目的classes路径<BR/>
     * 工具运行为:   /C:/Users/刘宇浩/git/rent/target/classes/<BR/>
     * jar包运行为: /F:/桌面/rent.jar!/BOOT-INF/classes!/<BR/>
     * war包运行为: /opt/software/tomcat/webapps/rent/WEB-INF/classes/<BR/>
     */
    public static final String CLASSES_PATH = getProjectClassesPath();
    /** 项目的打包方式 */
    public static EPackageType PACKAGE_TYPE;
    /** 当前项目路径 */
    public static final String PROJECT_PATH = getProjectPath();
    /** 打Jar包后的项目路径 */
    public static final String JAR_FILE_PATH = getJarFilePath();
    /** 打War包后的项目路径 */
    public static final String WAR_FILE_PATH = getWarFilePath();
    /** pom.xml的文件名 */
    private static final String POM_FILE_NAME = "pom.xml";
    /** ROOT */
    public static final String ROOT = "ROOT";
    /** 占位符左边界 */
    private static final String PLACEHOLDER_LEFT = "${";
    /** 占位符右边界 */
    private static final String PLACEHOLDER_RIGHT = "}";

    static {
        // 初始化Model对象
        MODEL = getModel();

        // 替换占位符
        setGroupId();
        setArtifactId();
        setPackaging();
        setFinalName();

        // 初始化打包类型
        setPackageType();
        log.info("pom.xml文件信息已加载到 MavenUtil 工具类中");
    }

    /**
     * 获取GroupId
     * 
     * @return
     */
    public static String getGroupId() {
        return MODEL.getGroupId();
    }

    /**
     * 设置GroupId
     * 
     * @return
     */
    public static void setGroupId() {
        MODEL.setGroupId(getProperties(MODEL.getGroupId()));
    }

    /**
     * 获取ArtifactId
     * 
     * @return
     */
    public static String getArtifactId() {
        return MODEL.getArtifactId();
    }

    /**
     * 设置ArtifactId
     * 
     * @return
     */
    public static void setArtifactId() {
        MODEL.setArtifactId(getProperties(MODEL.getArtifactId()));
    }

    /**
     * 获取打包的类型
     * 
     * @return
     */
    public static String getPackaging() {
        return MODEL.getPackaging();
    }

    /**
     * 设置打包的类型
     * 
     * @return
     */
    public static void setPackaging() {
        MODEL.setPackaging(getProperties(MODEL.getPackaging()));
    }

    /**
     * 获取打包后的包名
     * 
     * @return
     */
    public static String getFinalName() {
        return MODEL.getBuild().getFinalName();
    }

    /**
     * 设置打包后的包名
     * 
     * @return
     */
    public static void setFinalName() {
        Build build = MODEL.getBuild();
        if (StringUtil.isBlankObject(build)) {
            return;
        }

        String properties = getProperties(build.getFinalName());
        if (properties.startsWith("/")) {
            properties = properties.substring(1);
        }
        build.setFinalName(properties);
    }

    /**
     * 获取Model对象
     * 
     * @return
     */
    private static Model getModel() {
        Model model = null;
        if (fileIsJar()) {
            // jar包
            model = getModelJar();
        } else if (fileIsWar()) {
            // war包
            model = getModelWar();
        } else {
            // 此为开发环境
            model = getModelTest();
        }
        return model;
    }

    /**
     * 从当前项目中获取Model
     * 
     * @return
     */
    private static Model getModelTest() {
        try {
            // 获取Model
            return new MavenXpp3Reader().read(new FileReader(POM_FILE_NAME));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 从Jar包获取Model
     * 
     * @return
     */
    private static Model getModelJar() {
        JarFile jarFile = null;
        InputStream inputStream = null;
        try {
            // 获取JarFile
            jarFile = getJarFile();
            // 获取所有文件路径
            Enumeration<JarEntry> entries = jarFile.entries();
            // 遍历所有文件路径
            while (entries.hasMoreElements()) {
                JarEntry jarEntry = entries.nextElement();
                String entryPath = jarEntry.toString();
                // 如果是pom.xml就获取
                if (entryPath.startsWith("META-INF/maven/") && entryPath.endsWith(POM_FILE_NAME)) {
                    // 获取pom.xml的输入流
                    inputStream = jarFile.getInputStream(jarEntry);
                    // 获取Model
                    return new MavenXpp3Reader().read(inputStream);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            closeStream(inputStream);
            try {
                jarFile.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 从War包获取Model
     * 
     * @return
     */
    private static Model getModelWar() {
        // 用来存放pom.xml文件路径
        List<File> pomPathList = new ArrayList<>();

        // 获取META-INF文件路径
        String basePath = WAR_FILE_PATH + "/META-INF";

        // 获取pom.xml文件路径
        getModelWar(new File(basePath), pomPathList);

        // 获取Model
        return getModel(pomPathList.get(0));
    }

    /**
     * 从War包遍历获取Model
     * 
     * @param file
     * @param pomPath   获取到的pom.xml
     */
    private static void getModelWar(File file, List<File> pomPath) {
        // 如果是文件就判断是不是pom.xml
        if (file.isFile()) {
            if (POM_FILE_NAME.equals(file.getName())) {
                pomPath.add(file);
            }
            return;
        }

        // 如果是目录就去里面找pom.xml
        File[] listFiles = file.listFiles();
        for (File f : listFiles) {
            getModelWar(f, pomPath);
        }
    }

    /**
     * 获取Model对象
     * 
     * @param pom pom.xml文件
     * @return
     */
    private static Model getModel(File pom) {
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(pom);
            return new MavenXpp3Reader().read(inputStream);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closeStream(inputStream);
        }
        return null;
    }

    /**
     * 获取当前项目的Jar包文件
     * 
     * @return
     */
    public static JarFile getJarFile() {
        return getJarFile(JAR_FILE_PATH);
    }

    /**
     * 获取Jar包文件
     * 
     * @param filePath
     * @return
     */
    public static JarFile getJarFile(String filePath) {
        try {
            return new JarFile(filePath);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取属性
     */
    private static String getProperties(String val) {
        // 是否为null
        if (StringUtil.isBlankObject(val)) {
            return val;
        }

        // 不包含占位符就直接返回
        if (!val.startsWith(PLACEHOLDER_LEFT) || !val.endsWith(PLACEHOLDER_RIGHT)) {
            return val;
        }

        // 获取站位符中的Key
        String key = val.substring(PLACEHOLDER_LEFT.length(), val.length() - 1);

        // 包含占位符就从属性里获取
        Properties properties = MODEL.getProperties();

        String property = properties.getProperty(key);
        if (StringUtil.isNotBlankObject(property)) {
            if (!property.startsWith(PLACEHOLDER_LEFT) || !property.endsWith(PLACEHOLDER_RIGHT)) {
                return property;
            }
            key = property.substring(PLACEHOLDER_LEFT.length(), property.length() - 1);
        }

        property = Constant.PROPERTIES.getProperty(key);
        if (StringUtil.isNotBlankObject(property)) {
            return property;
        }

        return val;
    }

    /**
     * 设置当前项目的打包类型
     * 
     * @return
     */
    public static void setPackageType() {
        String packaging = MavenUtil.getPackaging();
        EPackageType[] values = EPackageType.values();
        for (EPackageType ePackageType : values) {
            if (ePackageType.getSuffix().equals(packaging)) {
                PACKAGE_TYPE = ePackageType;
            }
        }
    }

    /**
     * 获取当前项目的classes路径
     * 
     * @return
     */
    private static String getProjectClassesPath() {
        String classpath = null;
        try {
            classpath = PropertiesUtil.getResource("").getURL().getPath().replace("\\", "/");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 如果开头是file:就去掉
        String file = "file:";
        if (classpath.startsWith(file)) {
            classpath = classpath.substring(file.length());
        }
        return classpath;
    }

    /**
     * 获取当前正在执行的文件的classes路径
     * 
     * @return
     */
    public static String getFileClassesPath() {
        String decodeStr =
            decodeStr(MavenUtil.class.getProtectionDomain().getCodeSource().getLocation().getPath().replace("\\", "/"));

        // 如果开头是file:就去掉
        String file = "file:";
        if (decodeStr.startsWith(file)) {
            decodeStr = decodeStr.substring(file.length());
        }
        return decodeStr;
    }

    /**
     * 获取当前项目的根路径
     * 
     * @return
     */
    private static String getProjectPath() {
        return System.getProperty("user.dir").replace("\\", "/");
    }

    /**
     * 获取当前项目的jar包路径
     * 
     * @return
     */
    private static String getJarFilePath() {
        int indexOf = CLASSES_PATH.indexOf("!/BOOT-INF/");
        if (indexOf > 0) {
            return CLASSES_PATH.substring(0, indexOf);
        }
        return CLASSES_PATH;
    }

    /**
     * 获取当前项目的war包路径
     * 
     * @return
     */
    private static String getWarFilePath() {
        int indexOf = CLASSES_PATH.indexOf("/WEB-INF/");
        if (indexOf > 0) {
            return CLASSES_PATH.substring(0, indexOf);
        }
        return CLASSES_PATH;
    }

    /**
     * 判断文件是否是Jar包</BR>
     * 
     * @return
     */
    public static boolean fileIsJar() {
        return fileIsJar(CLASSES_PATH);
    }

    /**
     * 判断文件是否是Jar包</BR>
     * 
     * @param filePath
     * @return
     */
    public static boolean fileIsJar(String filePath) {
        int indexOf = filePath.indexOf("!/BOOT-INF/");
        if (indexOf < 0) {
            return false;
        } else {
            filePath = filePath.substring(0, indexOf);
        }
        return filePath.indexOf(EPackageType.JAR.getSuffixD()) > -1;
    }

    /**
     * 判断文件是否是War包</BR>
     * 
     * @return
     */
    public static boolean fileIsWar() {
        return fileIsWar(CLASSES_PATH);
    }

    /**
     * 判断文件是否是War包</BR>
     * 
     * @return
     */
    public static boolean fileIsWar(String filePath) {
        // 打War包后项目路径里包含的路径
        String warIncludePath = "/WEB-INF/";
        return filePath.indexOf(warIncludePath) > -1;
    }

    /**
     * 获取相对于jar包的资源路径
     * 
     * @return
     */
    public static String getJarFileResourcePath(String path) {
        return getFileResourcePath(path, true, false);
    }

    /**
     * 获取相对于war包的资源路径
     * 
     * @return
     */
    public static String getWarFileResourcePath(String path) {
        return getFileResourcePath(path, false, true);
    }

    /**
     * 获取资源文件的资源路径
     * 
     * @param path
     * @return
     */
    public static String getFileResourcePath(String path) {
        return getFileResourcePath(path, fileIsJar(), fileIsWar());
    }

    /**
     * 获取资源文件的资源路径
     * 
     * @param path
     * @param fileIsJar 当前项目是否在jar包中运行
     * @param fileIsWar 当前项目是否在war包中运行
     * @return
     */
    public static String getFileResourcePath(String path, boolean fileIsJar, boolean fileIsWar) {
        // 如果开头是file:就去掉
        String file = "file:";
        if (path.startsWith(file)) {
            path = path.substring(file.length());
        }

        // 判断指定项目是否是打包运行的
        if (fileIsJar || fileIsWar) {
            // 判断文件是否存在于jar包中
            if (fileProjectPathIsJar(path, fileIsJar)) {
                String jar = EPackageType.JAR.getSuffixD() + "!";
                path = path.substring(path.lastIndexOf(jar) + jar.length());
            } else {
                path = "/" + path.replaceFirst(MavenUtil.CLASSES_PATH, "");
            }
        } else {
            // 判断文件是否存在于jar包中
            if (MavenUtil.fileProjectPathIsJar(path, fileIsJar)) {
                String jar = EPackageType.JAR.getSuffixD() + "!";
                path = path.substring(path.lastIndexOf(jar) + jar.length());
            } else {
                String targetClasses = "/target/classes/";
                path = path.substring(path.indexOf(targetClasses) + targetClasses.length() - 1);
            }
        }
        return path;
    }

    /**
     * 判断文件在项目中的路径是否是Jar包</BR>
     * 
     * @param filePath
     * @return
     */
    public static boolean fileProjectPathIsJar(String filePath) {
        return fileProjectPathIsJar(filePath, fileIsJar());
    }

    /**
     * 判断文件在项目中的路径是否是Jar包</BR>
     * 
     * @param filePath
     * @param projectIsJar  项目是否在jar包中运行
     * @return
     */
    public static boolean fileProjectPathIsJar(String filePath, boolean projectIsJar) {
        String jar = EPackageType.JAR.getSuffixD() + "!";
        if (projectIsJar) {
            return filePath.indexOf(jar) != filePath.lastIndexOf(jar);
        } else {
            return filePath.indexOf(jar) > -1;
        }
    }

    /**
     * 判断文件路径是否是War包</BR>
     * 
     * @return
     */
    public static boolean filePathIsWar(String filePath) {
        // 打War包后项目路径里包含的路径
        String warIncludePath = "/WEB-INF/";
        return filePath.indexOf(warIncludePath) > -1;
    }

    /**
     * 关闭输入流
     * 
     * @param inputStream
     */
    private static void closeStream(InputStream inputStream) {
        try {
            if (StringUtil.isNotBlankObject(inputStream)) {
                inputStream.close();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    /**
     * 使用UTF-8编码将字符串转换为application/x-www-form-urlencodedformat
     * 
     * @param url
     * @return
     * @throws IOException
     */
    private static String decodeStr(String text) {
        try {
            return StringUtil.decode(text);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return text;
    }

}